/*
 Copyright (C) 2010 Kristian Duske

 This file is part of TrenchBroom.

 TrenchBroom is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 TrenchBroom is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
 */

#include "RenderBatch.h"

#include "render/Renderable.h"
#include "render/VboManager.h"

#include "kd/contracts.h"
#include "kd/vector_utils.h"

namespace tb::render
{
namespace
{

class IndexedRenderableWrapper : public IndexedRenderable
{
private:
  IndexedRenderable& m_wrappee;

public:
  IndexedRenderableWrapper(VboManager&, IndexedRenderable& wrappee)
    : m_wrappee{wrappee}
  {
  }

private:
  void prepareVerticesAndIndices(VboManager& vboManager) override
  {
    m_wrappee.prepareVerticesAndIndices(vboManager);
  }

  void doRender(RenderContext& renderContext) override
  {
    m_wrappee.render(renderContext);
  }
};

} // namespace

RenderBatch::RenderBatch(VboManager& vboManager)
  : m_vboManager{vboManager}
{
}

RenderBatch::~RenderBatch()
{
  kdl::vec_clear_and_delete(m_oneshots);
  kdl::vec_clear_and_delete(m_indexedRenderables);
}

void RenderBatch::add(Renderable* renderable)
{
  doAdd(renderable);
}

void RenderBatch::add(DirectRenderable* renderable)
{
  doAdd(renderable);
  m_directRenderables.push_back(renderable);
}

void RenderBatch::add(IndexedRenderable* renderable)
{
  auto* wrapper = new IndexedRenderableWrapper{m_vboManager, *renderable};
  doAdd(wrapper);
  m_indexedRenderables.push_back(wrapper);
}

void RenderBatch::addOneShot(Renderable* renderable)
{
  doAdd(renderable);
  m_oneshots.push_back(renderable);
}

void RenderBatch::addOneShot(DirectRenderable* renderable)
{
  doAdd(renderable);
  m_directRenderables.push_back(renderable);
  m_oneshots.push_back(renderable);
}

void RenderBatch::addOneShot(IndexedRenderable* renderable)
{
  auto* wrapper = new IndexedRenderableWrapper{m_vboManager, *renderable};
  doAdd(wrapper);
  m_indexedRenderables.push_back(wrapper);
  m_oneshots.push_back(renderable);
}

void RenderBatch::render(RenderContext& renderContext)
{
  prepareRenderables();
  renderRenderables(renderContext);
}

void RenderBatch::doAdd(Renderable* renderable)
{
  contract_pre(renderable != nullptr);

  m_batch.push_back(renderable);
}

void RenderBatch::prepareRenderables()
{
  for (auto* renderable : m_directRenderables)
  {
    renderable->prepareVertices(m_vboManager);
  }
  for (auto* renderable : m_indexedRenderables)
  {
    renderable->prepareVerticesAndIndices(m_vboManager);
  }
}

void RenderBatch::renderRenderables(RenderContext& renderContext)
{
  for (auto* renderable : m_batch)
  {
    renderable->render(renderContext);
  }
}

} // namespace tb::render
